import {AxiosPromise, AxiosRequestConfig, AxiosResponse, Method, RejectedFn, ResolvedFn} from "../types/index";

import dispatchRequest, {transformURL} from "./dispatchRequest";
import InterceptorManager from "./InterceptorManager";
import mergeConfig from "./mergeConfig";

interface Interceptors{
    request: InterceptorManager<AxiosRequestConfig>
    response: InterceptorManager<AxiosResponse>
}

interface PromiseChain<T>{
    resolved: ResolvedFn<T> | ((config: AxiosRequestConfig)=>void)
    rejected?: RejectedFn
}

export default  class  Axios{
    defauts: AxiosRequestConfig
    interceptors: Interceptors
    constructor(initConfig: AxiosRequestConfig) {
        this.defauts = initConfig
        this.interceptors = {
            request: new InterceptorManager<AxiosRequestConfig>(),
            response: new InterceptorManager<AxiosResponse>()
        }
    }

    request(url: any,config?: any): AxiosPromise{
        if(typeof url === 'string'){
            if(!config){
                config = {}
            }
            config.url = url
        }else {
            config = url
        }

        config = mergeConfig(this.defauts, config)

        const chain: PromiseChain<any>[] = [{
            resolved: dispatchRequest,
            rejected: undefined
        }]

        this.interceptors.request.forEach(interceptor=>{
            chain.unshift(interceptor)
        })
        this.interceptors.response.forEach(interceptor=>{
            chain.push(interceptor)
        })

        let promise = Promise.resolve(config)
        while(chain.length){
            const {resolved, rejected} = chain.shift()!
            promise = promise.then(resolved, rejected)
        }

        return promise
    }

    get(url: string, config?: AxiosRequestConfig): AxiosPromise{
        return this._requestMethodWithoutData('get', url, config)
    }
    delete(url: string, config?: AxiosRequestConfig): AxiosPromise{
        return this._requestMethodWithoutData('delete', url, config)
    }
    head(url: string, config?: AxiosRequestConfig): AxiosPromise{
        return this._requestMethodWithoutData('head', url, config)
    }
    options(url: string, config?: AxiosRequestConfig): AxiosPromise{
        return this._requestMethodWithoutData('options', url, config)
    }

    post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise{
        return this._requestMethodWithData('POST', url, data, config)
    }
    put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise{
        return this._requestMethodWithData('put', url, data, config)
    }
    patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise{
        return this._requestMethodWithData('put', url, data, config)
    }

    getUri(config?: AxiosRequestConfig): string{
        config = mergeConfig(this.defauts, config)
        return transformURL(config)
    }

    _requestMethodWithoutData(method: Method, url: string, config?: AxiosRequestConfig): AxiosPromise{
        return this.request(Object.assign(config||{}, {
            method: 'delete',
            url
        }))
    }
    _requestMethodWithData(method: Method, url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise{
        return this.request(Object.assign(config||{}, {
            method: 'delete',
            url,
            data
        }))
    }
}